/**
 * @describe 组件基类
 * @author 游金宇(KM)
 * @date 2023-09-19 12:32:22
 */

import { Component, Layers, Node, Prefab, Quat, Vec3, instantiate, isValid, log, math } from 'cc';
import { autorun, IReactionDisposer, IReactionOptions, IReactionPublic, IWhenOptions, Lambda, makeAutoObservable, makeObservable, observable, reaction, toJS, when } from '@shimotsuki/mobx';
import EventEmitter from 'eventemitter3';


// 定义一个类型，确保 T 不是 null 或 undefined，并且是一个对象类型
type NonNullableObject<T> = T extends null | undefined ? never : T extends Object ? T : never;


// 提取 reaction 函数的参数类型
type ReactionParameters<T, FireImmediately extends boolean = false> = Parameters<typeof reaction<T, FireImmediately>>;


type HOOK_NAME = 'destroyed' | 'started'


export type HOOK = { [key in HOOK_NAME]?: () => void }


export interface IUIOption<T extends object = {}, U extends object = {}> {
    /**组件传值间属性值 */
    props?: T;
    /**组件数据 */
    data?: U
    /**钩子 */
    hook?: HOOK
}


interface EE {
    context?: any;
    fn: (...args: any[]) => void;
    once: boolean;
}




/**参数 */
/**initUI->onAutoObserver->onLoad->start */
export class BaseComponent<T extends object = {}, K extends object = {}> extends Component {

    /**组件(传值)属性 */
    props: T = {} as T

    /**自由属性 */
    data: K = {} as K

    cat: typeof globalThis._cat = globalThis._cat

    /**autorun销毁 */
    private autorunDisposers: IReactionDisposer[] = []

    /**reaction销毁 */
    private reactionDisposers: IReactionDisposer[] = []

    private eventEmitter: EventEmitter[] = []

    hook: HOOK = {
        destroyed: () => { },
        started: () => { },
    }


    /**初始化UI(可在本函数中进行节点赋值或注册节点事件等...) */
    protected initUI() {

    }

    protected override __preload() {
        this.initUI()
        this.onAutoObserver()
    }

    constructor() {
        super();
        makeObservable(this, {
            props: observable,
            data: observable
        })
    }

    /**依赖收集生命周期(可以在该生命周期中注册依赖收集回调) */
    protected onAutoObserver() {

    }

    /**添加自动收集 */
    protected addAutorun(cb: (() => void) | (() => void)[]) {
        const cbs = Array.isArray(cb) ? cb : [cb]
        cbs.forEach(item => {
            const disposer = autorun(item)
            this.autorunDisposers.push(disposer)
        })
        return this
    }

    /**添加自动收集 */
    protected addReaction<T, FireImmediately extends boolean = false>(expression: (r: IReactionPublic) => T,
        effect: (arg: T, prev: FireImmediately extends true ? T | undefined : T, r: IReactionPublic) => void,
        opts?: IReactionOptions<T, FireImmediately>) {
        const disposer = reaction(expression, effect, opts);
        this.reactionDisposers.push(disposer)
        return this
    }
    // /**添加自动收集 */
    // protected addReaction<T, FireImmediately extends boolean = false>(option: ReactionParameters<T, FireImmediately>) {
    //     const disposer = reaction(...option)
    //     this.reactionDisposers.push(disposer)
    //     return this
    // }

    protected addAudoListener<E extends EventEmitter<any>>(eventEmitter: E): E {
        if (!this.eventEmitter.includes(eventEmitter) && eventEmitter !== this.cat.event) {
            this.eventEmitter.push(eventEmitter)
        }
        return eventEmitter
    }


    public override _onPreDestroy(): void {
        this.autorunDisposers.forEach((item) => {
            item()
        })
        this.reactionDisposers.forEach((item) => {
            item()
        })
        this.autorunDisposers = []
        this.reactionDisposers = []
        if (!super._onPreDestroy) {
            throw new Error("_onPreDestroy已弃用")
        } else {
            super._onPreDestroy()
        }

    }


    protected override onDisable(): void {
        this.onHide()

        // 清理当前组件所有全局注册的事件
        this.deleteAllEventByEmitter(this.cat.event)

        // 移除非全局注册的事件

        this.eventEmitter.forEach(item => {
            this.deleteAllEventByEmitter(item)
        })
        this.eventEmitter = []
        this.removeListener()
    }

    private deleteAllEventByEmitter(emitter: EventEmitter) {

        const events = (emitter as any)['_events'] as Record<string | symbol, EE | EE[]>;

        for (const key in events) {
            let handlers = events[key];

            if (!Array.isArray(handlers)) {
                handlers = [handlers]; // 统一处理，单个和数组情况
            }

            handlers.forEach((handler) => {
                if (handler.context === this) {
                    emitter.off(key, handler.fn, this); // 移除事件
                }
            })
        }
    }

    protected override onEnable(): void {
        this.onShow()
        this.addListener()
        this.onEventListener()
    }

    /**子类继承该方法(全局事件(this.cat.event)注册在components上的事件可以不添加取消监听(自动取消监听了)) */
    protected onEventListener() { }

    /**子类继承该方法 */
    protected addListener() { }

    /**子类继承该方法 */
    protected removeListener() { }


    /**
     * 添加到父节点指定层级
     * @param parent 父节点
     * @param index 插入的层级索引
     * @param options 参数
     * @returns 自身组件
     */
    addToParent(parent: Node | Prefab | Component, index: number, options?: IUIOption<T, K>): this;
    addToParent(parent: Node | Prefab | Component, options?: IUIOption<T, K>): this;
    addToParent(
        parent: Node | Prefab | Component,
        indexOrOptions?: number | IUIOption<T, K>,
        maybeOptions?: IUIOption<T, K>
    ) {

        const _parent = parent instanceof Prefab
            ? instantiate(parent)
            : parent instanceof Component
                ? parent.node
                : parent;

        if (typeof indexOrOptions === 'number') {
            const index = indexOrOptions;
            const options = maybeOptions;
            this.setOptions(options);
            _parent.insertChild(this.node, index);
        } else {
            const options = indexOrOptions;
            this.setOptions(options);
            _parent.addChild(this.node);
        }
        return this
    }

    /**设置属性(覆盖原有属性) */
    setOptions(options?: IUIOption<T, K>) {
        // 使用类型断言来确保 options 的正确类型
        if (!options) return
        // 如果 options 是 IUIOption<T> 类型，则可以安全地访问 props 属性
        for (let key in options) {
            switch (key as keyof IUIOption) {
                case 'hook':
                    options.hook && (this.hook = options.hook)
                    break;
                case 'props':
                    if (options.props !== null && typeof options.props === "object") {
                        this.props = options.props
                    }
                    break;
                case 'data':
                    if (options.data !== null && typeof options.data === "object") {
                        this.data = options.data
                    }
                    break;
                default:
                    break;
            }
        }
    }

    /**设置data属性(更新) */
    setUpdateData(data: Partial<K>) {
        if (!this.data) return
        data && Object.assign(this.data, this.toPlainObject(data));
        return this;
    }

    /**设置props属性(更新)*/
    setUpdateProps(props: Partial<T>) {
        // this.props的指向有可能变空
        if (!this.props) return

        props && Object.assign(this.props, this.toPlainObject(props));
        return this;
    }

    private toPlainObject(observableObj: any) {
        const result: Record<string, any> = {};
        for (const key of Reflect.ownKeys(observableObj)) {
            if (typeof key === "string") {
                try {
                    result[key] = observableObj[key];
                } catch { }
            }
        }
        return result;
    }

    /**从父节点移除并销毁 */
    removeAndDestroy() {
        if (isValid(this?.node)) {
            this.node.removeFromParent();
            this.node.destroy();
        }
    }

    /**设置坐标 */
    setPosition(positin: Vec3) {
        this.node.setPosition(positin)
        return this
    }


    setScale(scale: Vec3) {
        this.node.setScale(scale)
        return this
    }

    setAngle(angle: number) {
        this.node.angle = angle
        return this
    }

    setRotation(rotation: Quat) {
        this.node.setRotation(rotation)
        return this
    }

    setRotationFromEuler(rotation: math.Vec3) {
        this.node.setRotationFromEuler(rotation)
        return this
    }


    /**显示(同onEnable) */
    protected onShow() {

    }


    /**隐藏(同onDisable) */
    protected onHide() {

    }

    /**设置节点和子节点的层级 */
    setNodeAndChildrenLayer(layer: string | Layers.Enum) {
        setNodeAndChildrenLayer(this.node, layer)
        return this
    }



}
/**设置节点及子节点的层级 */
const setNodeAndChildrenLayer = (node: Node, layer: string | Layers.Enum) => {
    node.layer = (typeof layer === 'string' ? 2 ** Layers.nameToLayer(layer) : layer)
    node?.children.forEach(item => {
        setNodeAndChildrenLayer(item, layer)
    })
}
